package com.luxk.db.block;

import com.luxk.db.block.BlockTxHeader.BlockTxState;
import com.luxk.db.data.undo.UndoAddress;
import com.luxk.db.tx.Xid;
import com.luxk.util.ByteArrayUtil;

public class BlockTxHeaderHelper {
	
	/* format
	 * baseOffset + 0, 1 byte : block tx entry count
	 * baseOffset + 1, [22 * block tx count] byte : block tx entries
	 * 
	 * block tx entry format(23byte)
	 * 1byte : tx state
	 * 8byte : xid (usgmtId 4byte + wrapNo 4byte)
	 * 6byte : undo address (dba 4byte + rowNo 2byte)
	 * 8byte : commt scn/credit
	 */
	
	public static final int SIZE_BTE = 23;
	
	public static final int OFFSET_BTE_ARRAY_START = 1;
	
	public static final int OFFSET_STATE     = 0;
	public static final int OFFSET_XID       = 1; // usgmtId(4) + wrapNo(4)
	public static final int OFFSET_UNDO_ADDR = 9; // dba(4) + rowno(2)
	public static final int OFFSET_SCN       = 15;
	
	public static int getTxCount(byte[] buffer, int baseOffset)
			throws Exception {
		return buffer[baseOffset];
	}
	
	public static void setTxCount(byte[] buffer, int baseOffset, int cnt)
			throws Exception {
		if(cnt > Byte.MAX_VALUE)
			throw new Exception(
					"Maximum tx count exceeded(" + cnt + ")");
		
		buffer[baseOffset] = (byte)cnt;
	}
	
	public static BlockTxState getBlockTxState(byte[] buffer, int baseOffset,
			int i) throws Exception {
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE);
		return BlockTxState.getState(buffer[offset]);
	}
	
	public static void setBlockTxState(byte[] buffer, int baseOffset, int i,
			BlockTxState state)	throws Exception {
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE);
		buffer[offset] = (byte)state.getValue();
	}
	
	public static Xid getBlockTxId(byte[] buffer, int baseOffset, int i)
			throws Exception {
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) + 
				OFFSET_XID;
		int usgmtId = ByteArrayUtil.readInt(buffer, offset);
		int wrapNo = ByteArrayUtil.readInt(buffer, offset + 4);
		
		return new Xid(usgmtId, wrapNo);
	}
	
	public static void setBlockTxid(byte[] buffer, int baseOffset, int i, 
			int usgmtId, int wrapNo) throws Exception {

		if(wrapNo >= Integer.MAX_VALUE)
			throw new Exception(
					"Maximum wrap number exceed(" + wrapNo + ")");

		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) + 
				OFFSET_XID;
		ByteArrayUtil.writeInt(usgmtId, buffer, offset);
		ByteArrayUtil.writeInt(wrapNo, buffer, offset + 4);
	}
	
	public static UndoAddress getUndoAddress(byte[] buffer, int baseOffset, 
			int i) throws Exception {
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) + 
				OFFSET_UNDO_ADDR;
		
		int dba = ByteArrayUtil.readInt(buffer, offset);
		int rowNo = ByteArrayUtil.readInt(buffer, offset + 4);
		
		return new UndoAddress(dba, rowNo);
	}
	
	public static void setUndoAddress(byte[] buffer, int baseOffset, int i, 
			int dba, int rowNo) throws Exception {
		
		if(rowNo > Character.MAX_VALUE)
			throw new Exception(
					"Maximum row number exceed(" + rowNo + ")");
		
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) + 
				OFFSET_UNDO_ADDR;
		
		ByteArrayUtil.writeInt(dba, buffer, offset);
		ByteArrayUtil.writeInt(rowNo, buffer, offset + 4);
	}
	
	public static long getCommitScn(byte[] buffer, int baseOffset, 
			int i) throws Exception {
		
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) +
				OFFSET_STATE;
		BlockTxState txState = BlockTxState.getState(buffer[offset]);
		if(txState == BlockTxState.INVALID || txState == BlockTxState.ACTIVE) {
			throw new Exception(
					"Invalid block tx state(" + txState + ")");
		}
		
		offset += OFFSET_SCN;
		return ByteArrayUtil.readLong(buffer, offset);
	}
	
	public static void setCommitScn(byte[] buffer, int baseOffset, int i,
			long scn) throws Exception {

		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) +
				OFFSET_SCN;
		ByteArrayUtil.writeLong(scn, buffer, offset);
	}
	
	public static int getCredit(byte[] buffer, int baseOffset, int i)
			throws Exception {
		
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) +
				OFFSET_STATE;
		BlockTxState txState = BlockTxState.getState(buffer[offset]);
		if(txState != BlockTxState.ACTIVE) {
			throw new Exception(
					"Invalid block tx state(" + txState + ")");
		}
		
		offset += OFFSET_SCN;
		return ByteArrayUtil.readChar(buffer, offset);		
	}
	
	public static void setCredit(byte[] buffer, int baseOffset, int i,
			int credit) throws Exception {
		
		if(credit > Character.MAX_VALUE)
			throw new Exception(
					"Maximum credit value exceed(" + credit + ")");
		
		int cnt = BlockTxHeaderHelper.getTxCount(buffer, baseOffset);
		if(i >= cnt)
			throw new Exception(
					"Invalid block tx entry index(" + i + ", max " + cnt + ")");
		
		int offset = baseOffset + OFFSET_BTE_ARRAY_START + (i * SIZE_BTE) +
				OFFSET_SCN;
		ByteArrayUtil.writeChar((char)credit, buffer, offset);
	}
	
	public static int getTxHeaderSize(byte[] buffer, int baseOffset)
			throws Exception {
		return (1 + (BlockTxHeaderHelper.getTxCount(buffer, baseOffset) *
						SIZE_BTE));
	}
	
	public static int extendTxHeader(byte[] buffer, int baseOffset, int incSize)
			throws Exception {
		return 0;
	}
	
	public static int shrinkTxHeader(byte[] buffer, int baseOffset, int decSize)
			throws Exception {
		return 0;
	}
}